[iOS 10] CoreImageのCIFilterを試してみた
CoreImage
こんにちは!
モバイルアプリサービス部の田中孝明です。
CoreImageについては歴史が古く、iOS 5のころから存在し、バージョンが上がるたびに拡張されてきたフレームワークです。
iOS 10においても、WWDC 2016のセッションLive Photo Editing and RAW Processing with Core Imageで解説されていますとおり、新規に拡張された機能を確認することができます。
現時点で180を超えるフィルターと、RAWイメージの加工がサポートされており、画像編集アプリをよりリッチに彩ることができるようになっています。
WWDC 2016でも触れられていましたが、サンプルコード「RawExpose: Using CIRAWFilter to Decode RAW Images」で機能を簡単に試すことができました。
しかし、執筆当時の9/14時点では公開が停止されています。
CIFilter
画像の加工をする際はCIFilter経由で行います。
以下のコードはアルバム内の写真をPhotos.frameworkの機能を使ってPHAsset
として取得し、
PHImageManager
を使用して写真の情報を取得します。
取得した写真の情報をもとにCIFilter
を生成しています。
var asset : PHAsset? var originalTemp : Float = 0.0 var originalTint : Float = 0.0 var ciRawFilter : CIFilter? ... PHImageManager.default().requestImageData(for: asset, options: options) { imageData, dataUTI, _, _ in guard let imageData = imageData, let dataUTI = dataUTI else { return } let rawOptions = [ String(kCGImageSourceTypeIdentifierHint) : dataUTI ] self.ciRawFilter = CIFilter(imageData: imageData as Data, options: rawOptions) guard let ciRawFilter = self.ciRawFilter else { return } if let value = ciRawFilter.value(forKey: kCIOutputNativeSizeKey) as? CIVector { self.imageNativeSize = CGSize(width: value.x, height: value.y) } if let value = ciRawFilter.value(forKey: kCIInputNeutralTemperatureKey) { self.originalTemp = (value as AnyObject).floatValue self.tempSlider.setValue((value as AnyObject).floatValue, animated: false) } if let value = ciRawFilter.value(forKey: kCIInputNeutralTintKey) { self.originalTint = (value as AnyObject).floatValue self.tintSlider.setValue((value as AnyObject).floatValue, animated: false) } }
CIFilterの結果をリアルタイムに画面に反映させるためにGLKView
を用います。この辺りはサンプルコード「RawExpose: Using CIRAWFilter to Decode RAW Images」を参考にしています。
class ImageViewController: UIViewController { @IBOutlet weak var imageView : GLKView! var ciContext : CIContext? ... imageView.context = EAGLContext(api : .openGLES3) ciContext = CIContext(eaglContext: imageView.context, options: [kCIContextWorkingFormat : Int(kCIFormatRGBAh)]) ... } // MARK: - GLKViewDelegate extension ImageViewController: GLKViewDelegate { func glkView(_ view: GLKView, drawIn rect: CGRect) { guard let context = ciContext, let ciRawFilter = ciRawFilter, let imageNativeSize = imageNativeSize else { return } glClearColor(0.0, 0.0, 0.0, 1.0) glClear(GLbitfield(GL_COLOR_BUFFER_BIT)) glEnable(GLenum(GL_BLEND)) glBlendFunc(GLenum(GL_ONE), GLenum(GL_ONE_MINUS_SRC_ALPHA)) let contentScaledRect = rect.applying(CGAffineTransform(scaleX: view.contentScaleFactor, y: view.contentScaleFactor)) let scale = min(contentScaledRect.width / imageNativeSize.width, contentScaledRect.height / imageNativeSize.height) ciRawFilter.setValue(scale, forKey: kCIInputScaleFactorKey) var displayRect = CGRect(x:0, y:0, width:imageNativeSize.width, height:imageNativeSize.height).applying(CGAffineTransform(scaleX: scale, y: scale)) displayRect.origin.x = (contentScaledRect.width - displayRect.width) / 2.0 displayRect.origin.y = (contentScaledRect.height - displayRect.height) / 2.0 guard let image = ciRawFilter.outputImage else { return } context.draw(image, in:displayRect, from:image.extent) } }
あとは任意のフィルターをかけていきます。
// EV Filter func exposureAdjusted(value: Double) { guard let ciRawFilter = ciRawFilter else { return } ciRawFilter.setValue(value, forKey: kCIInputEVKey) imageView.setNeedsDisplay() }
// NeutralTemperature Filter func temperatureAdjusted(value: Double) { guard let ciRawFilter = ciRawFilter else { return } ciRawFilter.setValue(value, forKey: kCIInputNeutralTemperatureKey) imageView.setNeedsDisplay() }
// NeutralTint Filter func tintAdjusted(value: Double) { guard let ciRawFilter = ciRawFilter else { return } ciRawFilter.setValue(sender.value, forKey: kCIInputNeutralTintKey) imageView.setNeedsDisplay() }
まとめ
iPhone 6s / iPhone 6s Plus / iPhone SE / iPad Pro 9.7はリアカメラが1200万画素に向上しており、来るiPhone 7 Plusではデュアルカメラを搭載するなど、iOS搭載端末のカメラの高解像度化が一気に進んできました。
ユーザーも開発者も写真アプリをより一層楽しめる環境になりつつあるのではないでしょうか。
参考文献
Live Photo Editing and RAW Processing with Core Image
CoreImage Changes for Objective-C